% coding: utf-8

\subsection{Gramática.g}

\begin{Verbatim}[frame=single, label=C\'odigo, numbers=left]
grammar gramatica;

@header{
 import java.util.HashSet;
 import java.util.HashMap;
 import java.lang.Math;
 
}

@members {
	public HashSet< Medida > unidadesDeclaradas = new HashSet< Medida >();
	public HashSet< Medida > variablesDeclaradas = new HashSet< Medida >();

	public class Medida {
		public String nombre = "null";
		public Float valor = Float.parseFloat("1");
		public HashMap<String,Integer> unidadCompuesta = new HashMap<String,Integer>();
		
		public Medida(String unNombre, Float unValor, HashMap<String,Integer> unaUnidadCompuesta)
		{
			this.nombre = unNombre;
			this.valor = unValor;
			for(String aKey : unaUnidadCompuesta.keySet())
			{
		 		this.unidadCompuesta.put(aKey, unaUnidadCompuesta.get(aKey));
			}
		}
		public Medida(){}
	}
	
	public void printMap(HashMap<String,Integer> unaUnidadCompuesta) {
	   System.out.print("Unidad compuesta: ");
	   for(String aKey : unaUnidadCompuesta.keySet())
	   {
	   	System.out.print(aKey + "^" + unaUnidadCompuesta.get(aKey) + " ");
	   }
	   if(unaUnidadCompuesta.keySet().size() == 0)
	   	System.out.print("Vacio");
	   System.out.print("\n");
	   System.out.print("\n");
	}
	
	public void printAsMedida(HashMap<String,Integer> unaUnidadCompuesta, float valor, String nombre)
	{
	/*
		System.out.print("Identificador: " + nombre); 
		System.out.print("\n"); 
		System.out.print("valor: " + String.valueOf(valor));
		System.out.print("\n");
		printMap(unaUnidadCompuesta);
		*/
	
	}
	
	public String printMedidaSimplificada(HashMap<String,Integer> unaUnidadCompuesta, float valor)
	{
		
		String ret = String.valueOf(valor);
		for(String aKey : unaUnidadCompuesta.keySet())
		{
		   	ret += " " + aKey + "^" + unaUnidadCompuesta.get(aKey) + " ";
		}
		return ret;
	}
	
	public Boolean setContainsItemWithName(HashSet< Medida > aSet, String aName) {
		for (Medida unaMedida : aSet) 
		{
		    if(unaMedida.nombre.equals(aName))
		    	return true;
		}
		return false;
	}
	
	public Medida getItemWithName(HashSet< Medida > aSet, String aName) {
		Medida ret = new Medida();
		for (Medida unaMedida : aSet) 
		{
		    if(unaMedida.nombre.equals(aName))
		    	ret = unaMedida;
		}
		return ret;
	}
	
	public void error(String unMensaje)
	{
		System.out.print("Error: " + unMensaje + "\n");
	}
		
}

// FRAGMENTOS DE TOKENS

fragment
CHAR:  'a'..'z' | 'A'..'Z'
    ;
    
fragment
DIGITO0A9
	:	 '0'..'9'
	;

// TOKENS DE LA GRAMATICA

VAR 	:	'var'
	;
	
UNIDADES:	'unidades'
	;

ID 	:	CHAR (CHAR | DIGITO0A9)*
	;
	
MAS	:	'+'
	;

MENOS	:	'-'
	;

POR	:	'*'
	;

DIV	:	'/'
	;

LB	:	'('
	;

RB	:	')'
	;

COMA	:	','
	;

DOTCOM	:	';'
	;

POT 	:	'^'
	;

IGUAL	:	'='
	;	

WS  : ( ' ' | '\t' | '\n' | '\r' )+   { $channel=HIDDEN; }   ;

ENTERO	:	DIGITO0A9+
	;

NUMERO :	(DIGITO0A9+ '.' DIGITO0A9+)
	;



//------------------------------- PRODUCCIONES----------------------------------------------

programa
	: 
	(sentencia DOTCOM)*
	;

sentencia
	:
	UNIDADES identificadores 
	{
		
		System.out.print("Sentencia: " + $UNIDADES.text + " " + $identificadores.text + ";\n"); 
		if($identificadores.errorSemantico)
			error($identificadores.errorTexto);
	}
	| VAR ID IGUAL expr 
	{
		if(!$expr.errorSemantico)
		{
			if(setContainsItemWithName(unidadesDeclaradas,$ID.text))
			{
				System.out.print(
					"Sentencia: " + $VAR.text + " " + $ID.text + " " + "= "+ $expr.text + ";\n"
				); 
				error("La variable " + $ID.text + " ya fue declarada como unidad");
			}
			else if(setContainsItemWithName(variablesDeclaradas,$ID.text))
			{
				System.out.print(
					"Sentencia: " + $VAR.text + " " + $ID.text + " " + "= "+ $expr.text + ";\n"
				); 
				error("La variable " + $ID.text + " ya fue declarada como variable");
			}
			else
			{
				System.out.print(
					"Sentencia: " + $VAR.text + " " + $ID.text + " " + "= "+ 
					printMedidaSimplificada($expr.unidadCompuesta, $expr.valor) + ";\n"
				); 
				Medida unID = new Medida();
				unID.valor = $expr.valor;
				unID.nombre = $ID.text; 
				unID.unidadCompuesta = $expr.unidadCompuesta;
				variablesDeclaradas.add(unID);
				printAsMedida(
					unID.unidadCompuesta,
					unID.valor,
					"Soy la variable "+ $ID.text + ", me acaban de declarar e instanciar!"
				);
				
			}
		}
		else
		{
			System.out.print(
				"Sentencia: " + $VAR.text + " " + $ID.text + " " + "= "+ $expr.text + ";\n"
			); 
			error($expr.errorTexto);
		}
	}
	| expr 
	{
		if(!$expr.errorSemantico)
		{
			System.out.print(
				"Sentencia: " + printMedidaSimplificada($expr.unidadCompuesta, $expr.valor) + ";\n"
			); 
			printAsMedida($expr.unidadCompuesta, $expr.valor, "Soy una simple expresión!");
		}
		else
		{
			System.out.print("Sentencia: " + $expr.text + ";\n");
			error($expr.errorTexto);
		}
	}
	;

identificadores returns [Boolean errorSemantico = false, String errorTexto = ""]
	:
	ID1 = ID 
	{
		if(setContainsItemWithName(unidadesDeclaradas,$ID1.text))
		{
			retval.errorSemantico = true;
			retval.errorTexto = "'" + $ID1.text + "' ya fue declarada como medida";
		}
		else if(setContainsItemWithName(variablesDeclaradas,$ID1.text))
		{
			retval.errorSemantico = true;
			retval.errorTexto = "'" + $ID1.text + "' ya fue declarada como variable";
		}
		else
		{
			Medida unID = new Medida();
			unID.unidadCompuesta.put($ID1.text, 1);
			unID.valor = Float.parseFloat("1");
			unID.nombre = $ID1.text; 
			unidadesDeclaradas.add(unID);
			printAsMedida(
				unID.unidadCompuesta,
				unID.valor,
				"Soy la unidad "+ $ID1.text + ", me acaban de declarar e instanciar!"
			);
		}
	}
	(COMA ID2 = ID
	{
		if(!retval.errorSemantico)
		{
			if(setContainsItemWithName(unidadesDeclaradas,$ID2.text))
			{
				
				retval.errorSemantico = true;
				retval.errorTexto = "'" + $ID2.text + "' ya fue declarada como medida.";
			}
			else if(setContainsItemWithName(variablesDeclaradas,$ID2.text))
			{
				retval.errorSemantico = true;
				retval.errorTexto = "'" + $ID2.text + "' ya fue declarada como variable.";
			}
			else
			{
				Medida otroID = new Medida();
				otroID.unidadCompuesta.put($ID2.text, 1);
				otroID.valor = Float.parseFloat("1");
				otroID.nombre = $ID2.text; 
				unidadesDeclaradas.add(otroID);
				printAsMedida(
					otroID.unidadCompuesta,
					otroID.valor,
					"Soy la unidad "+ $ID2.text + ", me acaban de declarar e instanciar!"
				);
			}
		}
	}
	)*
	;

expr 	returns [
			Float valor=Float.parseFloat("0"),
			int operacion = 1, 
			HashMap<String,Integer> unidadCompuesta = new HashMap<String,Integer>(), 
			Boolean errorSemantico = false, 
			String errorTexto = ""
		] 
	:
	e11 = e1 
	{
		retval.valor = $e11.valor;
		retval.unidadCompuesta = new HashMap<String,Integer>();
		for(String aKey : $e11.unidadCompuesta.keySet())
		{
	 		retval.unidadCompuesta.put(aKey, $e11.unidadCompuesta.get(aKey));
		}
		retval.errorSemantico = $e11.errorSemantico;
		retval.errorTexto = $e11.errorTexto;
	}  
	((MAS {retval.operacion = 1;} | MENOS {retval.operacion = 2;}) e12 = e1
	{
		printAsMedida(retval.unidadCompuesta, retval.valor, "Atentos a este chabon IZQ!");
		printAsMedida( $e12.unidadCompuesta,  $e12.valor, "Atentos a este chabon DER!");
		retval.errorSemantico = (retval.errorSemantico || $e12.errorSemantico);
		if($e12.errorSemantico)
		{
			retval.errorTexto = $e12.errorTexto;
		}
		if($e11.errorSemantico)
			retval.errorTexto = $e11.errorTexto;
		
		if(!retval.errorSemantico)
		{
			if($e12.valor != 0 && retval.valor != 0)
			{
				for(String aKey : $e12.unidadCompuesta.keySet())
				{
			 		if(!retval.unidadCompuesta.containsKey(aKey) || 
						retval.unidadCompuesta.get(aKey) != $e12.unidadCompuesta.get(aKey)
					)
			 		{
						retval.errorTexto = "Las unidades no coinciden.";
						retval.errorSemantico = true;
			 		}
				}
			}
			if(!retval.errorSemantico)
			{
				switch(retval.operacion){
					case 1:
						retval.valor = retval.valor + $e12.valor;
						break;
					case 2:
						retval.valor = retval.valor - $e12.valor;
						break;
					default:
						retval.errorSemantico = true;
						retval.errorTexto = "Opcion desconocida";
						break;
				}	
				if (retval.valor != 0)
				{
					if($e12.valor != 0)
					{
						retval.unidadCompuesta = $e12.unidadCompuesta;
					}
				}
				else
				{ 
					retval.unidadCompuesta = new HashMap<String,Integer>();
				}
				printAsMedida(
					retval.unidadCompuesta, 
					retval.valor, 
					"Soy una expresión, y me acaban de SUMAR o RESTAR!"
				);
			}
			
			
		}
	}
	)*
	;
		
e1      returns [
			Float valor=Float.parseFloat("0"), 
			int operacion = 1, 
			HashMap<String,Integer> unidadCompuesta = new HashMap<String,Integer>(), 
			Boolean errorSemantico = false, 
			String errorTexto = ""
		]
	:
	e21 = e2 
	{
		retval.valor = $e21.valor;
		retval.unidadCompuesta = new HashMap<String,Integer>();
		for(String aKey : $e21.unidadCompuesta.keySet())
		{
	 		retval.unidadCompuesta.put(aKey, $e21.unidadCompuesta.get(aKey));
		}
		retval.errorSemantico = $e21.errorSemantico;
		retval.errorTexto = $e21.errorTexto;
	} 
	(
		(
			POR {retval.operacion = 1;}
			|
		 	DIV {retval.operacion = 2;} 
		) 
	e22 = e2 
	{
		retval.errorSemantico = (retval.errorSemantico || $e22.errorSemantico);
		if($e22.errorSemantico)
		{
			retval.errorTexto = $e22.errorTexto;
		}
		if(retval.errorSemantico)
			retval.errorTexto = retval.errorTexto;
	
		if(!retval.errorSemantico)
			{
			switch(retval.operacion)
			{
			case 1:
				retval.valor = retval.valor * $e22.valor;
				if (retval.valor == 0)
					retval.unidadCompuesta = new HashMap<String,Integer>();
				else 
				{		
					if($e22.unidadCompuesta != null)
					{
						HashMap<String,Integer> auxKeysDeleted = new HashMap<String,Integer>();
						for(String aKey : retval.unidadCompuesta.keySet())
						{
					 		if($e22.unidadCompuesta.containsKey(aKey))
					 		{
					 			if((
									retval.unidadCompuesta.get(aKey) + 
									$e22.unidadCompuesta.get(aKey)
								) != 0)
									retval.unidadCompuesta.put(
										aKey, 
										retval.unidadCompuesta.get(aKey) + 
										$e22.unidadCompuesta.get(aKey)
									);
								else
									auxKeysDeleted.put(aKey, 1);
							}
						}
						
						for(String aKey : auxKeysDeleted.keySet())
						{
							retval.unidadCompuesta.remove(aKey);
						}
						
						for(String aKey : $e22.unidadCompuesta.keySet())
						{
					 		if(
								!retval.unidadCompuesta.containsKey(aKey) && 
								!auxKeysDeleted.containsKey(aKey)
							)
					 		{
								retval.unidadCompuesta.put(
									aKey, 
									$e22.unidadCompuesta.get(aKey)
								);
					 		}
						}
					}
				}
				break;
			case 2:
				retval.valor = retval.valor / $e22.valor;
				
				if($e22.unidadCompuesta != null)
				{
					HashMap<String,Integer> auxKeysDeleted = new HashMap<String,Integer>();
					for(String aKey : retval.unidadCompuesta.keySet())
					{
						if($e22.unidadCompuesta.containsKey(aKey))
				 		{
				 	 		if((
								retval.unidadCompuesta.get(aKey) - 
								$e22.unidadCompuesta.get(aKey)
								) != 0
							)
								retval.unidadCompuesta.put(
									aKey, 
									retval.unidadCompuesta.get(aKey) - 
									$e22.unidadCompuesta.get(aKey)
								);
							else
								auxKeysDeleted.put(aKey, 1);
							
				 		}
					}
					
					for(String aKey : auxKeysDeleted.keySet())
					{
						retval.unidadCompuesta.remove(aKey);
					}
					
					for(String aKey : $e22.unidadCompuesta.keySet())
					{
				 		if(
							!retval.unidadCompuesta.containsKey(aKey) && 
							!auxKeysDeleted.containsKey(aKey)
						)
				 		{
							retval.unidadCompuesta.put(
								aKey, 
								$e22.unidadCompuesta.get(aKey) * (-1)
							);
				 		}
					}
				}
	
			break;
			default:
				retval.errorSemantico = true;
				retval.errorTexto = "Opcion desconocida";
				break;
			}
			
			if(retval.operacion == 1)
				printAsMedida(
					retval.unidadCompuesta, 
					retval.valor, 
					"Soy una expresión, y me acaban de MULTIPLICAR!"
				);
			else
				printAsMedida(
					retval.unidadCompuesta, 
					retval.valor, 
					"Soy una expresión, y me acaban de DIVIDIR!"
				);
		}
	}
	| 
	e3
	{
		retval.errorSemantico = (retval.errorSemantico || $e3.errorSemantico);
		if($e3.errorSemantico)
		{
			retval.errorTexto = $e3.errorTexto;
		}
		
		if(!retval.errorSemantico)
		{
			retval.valor = retval.valor * $e3.valor;
			if($e3.unidadCompuesta != null)
			{
				HashMap<String,Integer> auxKeysDeleted = new HashMap<String,Integer>();
				for(String aKey : retval.unidadCompuesta.keySet())
				{
			 		if($e3.unidadCompuesta.containsKey(aKey))
			 		{
			 			if((
							retval.unidadCompuesta.get(aKey) + $e3.unidadCompuesta.get(aKey)
							) != 0
						)
							retval.unidadCompuesta.put(
								aKey, 
								retval.unidadCompuesta.get(aKey) + 
								$e3.unidadCompuesta.get(aKey)
							);
						else
							auxKeysDeleted.put(aKey, 1);
					}
			 		
				}
				
				for(String aKey : auxKeysDeleted.keySet())
				{
					retval.unidadCompuesta.remove(aKey);
				}
				
				for(String aKey : $e3.unidadCompuesta.keySet())
				{
			 		if(
						!retval.unidadCompuesta.containsKey(aKey) && 
						!auxKeysDeleted.containsKey(aKey)
					)
			 		{
						retval.unidadCompuesta.put(aKey, $e3.unidadCompuesta.get(aKey));
			 		}
				}
			}
			printAsMedida(
				retval.unidadCompuesta, 
				retval.valor, 
				"Soy una expresión, y me acaban de MULTIPLICAR!"
			);
		}
	}
	)* 
	;

e2 	returns [
		Float valor=Float.parseFloat("1"), 
		HashMap<String,Integer> unidadCompuesta = new HashMap<String,Integer>(), 
		Boolean errorSemantico = false, 
		String errorTexto = ""
	]
	:	
	{float signo = 1;}
	(MENOS {
		signo = signo * Float.parseFloat("-1");
	})* e3 {
		retval.valor = signo * $e3.valor; 
		retval.unidadCompuesta = new HashMap<String,Integer>();
		for(String aKey : $e3.unidadCompuesta.keySet())
		{
	 		retval.unidadCompuesta.put(aKey, $e3.unidadCompuesta.get(aKey));
		}
		retval.errorSemantico = $e3.errorSemantico;
		retval.errorTexto = $e3.errorTexto;
	}
	;

e3 	returns [
		int signo = 1, 
		Float valor=Float.parseFloat("0"), 
		HashMap<String,Integer> unidadCompuesta = new HashMap<String,Integer>(), 
		Boolean errorSemantico = false, 
		String errorTexto = ""
	]
	:
	e4 
	{
		Medida unaMedida = new Medida("SinNombre", $e4.valor,  $e4.unidadCompuesta);
		retval.valor = $e4.valor;
		retval.unidadCompuesta = new HashMap<String,Integer>();
		for(String aKey : $e4.unidadCompuesta.keySet())
		{
	 		retval.unidadCompuesta.put(aKey, $e4.unidadCompuesta.get(aKey));
		}
		retval.errorSemantico = $e4.errorSemantico;
		retval.errorTexto = $e4.errorTexto;
	} 
	(POT { retval.signo = 1; } (MENOS { retval.signo = -1; })? ENTERO {
		if(!retval.errorSemantico)
		{
			if(retval.unidadCompuesta.size() == 0 && Integer.parseInt($ENTERO.text) == 0)
			{
				retval.valor = Float.parseFloat("1");
			}
			else if(retval.unidadCompuesta.size() != 0 && Integer.parseInt($ENTERO.text) == 0)
			{
				retval.valor = Float.parseFloat("1");
				retval.unidadCompuesta = new HashMap();
			}
			else
			{
				retval.valor = (float) Math.pow(
					retval.valor, 
					Integer.parseInt($ENTERO.text) * retval.signo
				);
				for(String aKey : retval.unidadCompuesta.keySet())
				{
					retval.unidadCompuesta.put(
						aKey, 
						retval.unidadCompuesta.get(aKey) * Integer.parseInt($ENTERO.text) 
						* retval.signo
					);
				}
			}
			printAsMedida(
				retval.unidadCompuesta, 
				retval.valor, 
				"Soy una expresión, y me acaban de potenciar!"
			);
		}
		
	})*
	;


e4	returns [
		Float valor=Float.parseFloat("0"), 
		HashMap<String,Integer> unidadCompuesta = new HashMap<String,Integer>(), 
		Boolean errorSemantico = false, 
		String errorTexto = ""
	]
	:
	ENTERO 
	{
		retval.valor = Float.parseFloat($ENTERO.text);
		retval.unidadCompuesta = new HashMap<String,Integer>();
		printAsMedida(
			retval.unidadCompuesta, 
			retval.valor, 
			"Soy una ENTERO, y me redujeron a una expresion!"
		);
	}
	| 
 	NUMERO 
 	{
 		retval.unidadCompuesta = new HashMap<String,Integer>();
 		retval.valor = Float.parseFloat($NUMERO.text);
 		printAsMedida(
			retval.unidadCompuesta, 
			retval.valor, 
			"Soy una NUMERO, y me redujeron a una expresion!"
		);
 	}
 	| 
 	ID
 	{
 		if(setContainsItemWithName(unidadesDeclaradas,$ID.text))
 		{
 			Medida unaMedida = getItemWithName(unidadesDeclaradas,$ID.text);
 			retval.valor = unaMedida.valor;
 			retval.unidadCompuesta = unaMedida.unidadCompuesta;
 			printAsMedida(
				retval.unidadCompuesta, 
				retval.valor, 
				"Soy una UNIDAD (ID), y me volvieron a encontrar!"
			);
 		}
 		else if(setContainsItemWithName(variablesDeclaradas,$ID.text))
 		{
 			Medida unaMedida = getItemWithName(variablesDeclaradas,$ID.text);
 			retval.valor = unaMedida.valor;
 			retval.unidadCompuesta = unaMedida.unidadCompuesta;
 			printAsMedida(
				retval.unidadCompuesta, 
				retval.valor, 
				"Soy una VARIABLE (ID), y me volvieron a encontrar!"
			);
 		}
 		else
 		{
 			retval.errorSemantico = true;
 			retval.errorTexto = "'"+ $ID.text + "' no fue declarado como variable ni como unidad.";
 		}
 	}
 	| LB expr 
 	{
 		retval.valor = $expr.valor; 
 		retval.unidadCompuesta = $expr.unidadCompuesta;
 		retval.errorSemantico = $expr.errorSemantico;
 		retval.errorTexto = $expr.errorTexto;
 	} RB
	;

\end{Verbatim}



